package com.citywithincity.utils;

import java.util.Arrays;

import com.damai.hex.IPad;

public class HexUtil {
	public static String toHex(byte[] b) {
		StringBuilder sb = new StringBuilder();
		for (byte c : b) {
			sb.append(Integer.toBinaryString(c));
		}

		return sb.toString();
	}

	private final static char[] HEX = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E',
			'F' };

	public static String toHexString(byte[] d, int s, int n) {
		final char[] ret = new char[n * 2];
		final int e = s + n;

		int x = 0;
		for (int i = s; i < e; ++i) {
			final byte v = d[i];
			ret[x++] = HEX[0x0F & (v >> 4)];
			ret[x++] = HEX[0x0F & v];
		}
		return new String(ret);
	}

	/**
	 *
	 * @param data
	 * @param start
	 * @return
	 */
	public static short toShort(byte[] data, int start) {
		return (short) (((data[start + 1] << 8) & 0xff00) | (data[start] & 0xff));
	}

	public static int toInt(byte[] data, int start) {
		return ((data[start + 3] << 24) & 0xff000000) | ((data[start + 2] << 16) & 0xff0000)
				| ((data[start + 1] << 8) & 0xff00) | (data[start] & 0xff);
	}

	public static byte[] copy(byte[] bytes, int dataStart, short len) {
		return Arrays.copyOfRange(bytes, dataStart, len);
	}

	/**
	 * 用于建立十六进制字符的输出的小写字符数组
	 */
	private static final char[] DIGITS_LOWER = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd',
			'e', 'f' };
	/**
	 * 用于建立十六进制字符的输出的大写字符数组
	 */
	private static final char[] DIGITS_UPPER = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D',
			'E', 'F' };

	/**
	 * 将字节数组转换为十六进制字符数组
	 *
	 * @param data
	 *            byte[]
	 * @return 十六进制char[]
	 */
	public static char[] encodeHex(byte[] data) {
		return encodeHex(data, true);
	}

	/**
	 * 将字节数组转换为十六进制字符数组
	 *
	 * @param data
	 *            byte[]
	 * @param toLowerCase
	 *            <code>true</code> 传换成小写格式 ， <code>false</code> 传换成大写格式
	 * @return 十六进制char[]
	 */
	public static char[] encodeHex(byte[] data, boolean toLowerCase) {
		return encodeHex(data, data.length, toLowerCase ? DIGITS_LOWER : DIGITS_UPPER);
	}

	/**
	 * 将字节数组转换为十六进制字符数组
	 *
	 * @param data
	 *            byte[]
	 * @param toDigits
	 *            用于控制输出的char[]
	 * @return 十六进制char[]
	 */
	protected static char[] encodeHex(byte[] data, int l, char[] toDigits) {
		char[] out = new char[l << 1];
		// two characters form the hex value.
		for (int i = 0, j = 0; i < l; i++) {
			out[j++] = toDigits[(0xF0 & data[i]) >>> 4];
			out[j++] = toDigits[0x0F & data[i]];
		}
		return out;
	}

	protected static char[] encodeHex(byte[] data, int start, int l, char[] toDigits) {
		char[] out = new char[l << 1];
		// two characters form the hex value.
		for (int i = 0, j = 0; i < l; i++) {
			out[j++] = toDigits[(0xF0 & data[start + i]) >>> 4];
			out[j++] = toDigits[0x0F & data[start + i]];
		}
		return out;
	}

	/**
	 * 将字节数组转换为十六进制字符串
	 *
	 * @param data
	 *            byte[]
	 * @return 十六进制String
	 */
	public static String encodeHexStr(byte[] data) {
		return encodeHexStr(data, false);
	}

	public static String encodeHexStr(byte[] data, int start, int len) {
		return new String(encodeHex(data, start, len, DIGITS_LOWER));
	}

	public static String encodeHexStr(byte[] data, int len) {
		return encodeHexStr(data, len, false);
	}

	/**
	 * 将字节数组转换为十六进制字符串
	 *
	 * @param data
	 *            byte[]
	 * @param toLowerCase
	 *            <code>true</code> 传换成小写格式 ， <code>false</code> 传换成大写格式
	 * @return 十六进制String
	 */
	public static String encodeHexStr(byte[] data, boolean toLowerCase) {
		return encodeHexStr(data, data.length, toLowerCase ? DIGITS_LOWER : DIGITS_UPPER);
	}

	public static String encodeHexStr(byte[] data, int len, boolean toLowerCase) {
		return encodeHexStr(data, len, toLowerCase ? DIGITS_LOWER : DIGITS_UPPER);
	}

	/**
	 * 将字节数组转换为十六进制字符串
	 *
	 * @param data
	 *            byte[]
	 * @param toDigits
	 *            用于控制输出的char[]
	 * @return 十六进制String
	 */
	protected static String encodeHexStr(byte[] data, int len, char[] toDigits) {
		return new String(encodeHex(data, len, toDigits));
	}

	public static byte[] decodeHex(String data) {
		return decodeHex(data.toCharArray());
	}

	/**
	 * 将十六进制字符数组转换为字节数组
	 *
	 * @param data
	 *            十六进制char[]
	 * @return byte[]
	 * @throws RuntimeException
	 *             如果源十六进制字符数组是一个奇怪的长度，将抛出运行时异常
	 */
	public static byte[] decodeHex(char[] data) {
		int len = data.length;
		if ((len & 0x01) != 0) {
			throw new RuntimeException("Odd number of characters.");
		}
		byte[] out = new byte[len >> 1];
		// two characters form the hex value.
		for (int i = 0, j = 0; j < len; i++) {
			int f = toDigit(data[j], j) << 4;
			j++;
			f = f | toDigit(data[j], j);
			j++;
			out[i] = (byte) (f & 0xFF);
		}
		return out;
	}

	public static int decodeHex(char[] data, byte[] out, int pos) {
		int len = data.length;
		if ((len & 0x01) != 0) {
			throw new RuntimeException("Odd number of characters.");
		}
		// two characters form the hex value.
		for (int i = 0, j = 0; j < len; i++) {
			int f = toDigit(data[j], j) << 4;
			j++;
			f = f | toDigit(data[j], j);
			j++;
			out[i + pos] = (byte) (f & 0xFF);
		}
		return len;
	}

	public static int decodeHex(String value, byte[] bytes, int pos) {
		char[] chars = value.toCharArray();

		return decodeHex(chars, bytes, pos);

	}

	/**
	 * 将十六进制字符转换成一个整数
	 *
	 * @param ch
	 *            十六进制char
	 * @param index
	 *            十六进制字符在字符数组中的位置
	 * @return 一个整数
	 * @throws RuntimeException
	 *             当ch不是一个合法的十六进制字符时，抛出运行时异常
	 */
	protected static int toDigit(char ch, int index) {
		int digit = Character.digit(ch, 16);
		if (digit == -1) {
			throw new RuntimeException("Illegal hexadecimal character " + ch + " at index " + index);
		}
		return digit;
	}

	/**
	 * 整数转成指定从长度的十六进制数
	 * 
	 * @param value
	 * @param len
	 */
	public static String int2HexStr(int value, int len) {
		String result = Integer.toHexString(value);
		int i = result.length();
		if (i > len) {
			return result.substring(i - len);
		} else if (i < len) {
			// 前面补充0
			StringBuilder sb = new StringBuilder(len);
			for (int j = i; j < len; ++j) {
				sb.append('0');
			}
			sb.append(result);
			return sb.toString();
		} else {
			return result;
		}

	}

	/**
	 *
	 * @param b
	 * @param s
	 * @param n
	 * @return
	 */
	public static int toInt(byte[] b, int s, int n) {
		int ret = 0;

		final int e = s + n;
		for (int i = s; i < e; ++i) {
			ret <<= 8;
			ret |= b[i] & 0xFF;
		}
		return ret;
	}

	public static String formatHex(String str) {

		int len = str.length();
		StringBuilder sb = new StringBuilder();
		for (int i = 0; i < len; i += 8) {
			sb.append(str.substring(i, Math.min(i + 8, str.length())));
			sb.append(' ');
		}

		return sb.toString();

	}

	/**
	 * 二进制字符串转成byte[]
	 * 
	 * @param bin
	 * @return
	 */
	public static byte[] binStr2byteArray(String src) {
		int len = src.length() >> 3;
		byte[] result = new byte[len];
		for (int i = 0; i < len; ++i) {
			String bin = src.substring(i * 8, i * 8 + 8);
			result[i] = (byte) Integer.parseInt(bin, 2);
		}
		return result;
	}

	/**
	 * 
	 * @param encodeHex
	 * @return
	 */
	public static String hexDecimal(char[] encodeHex) {
		StringBuilder sb = new StringBuilder();
		for (char c : encodeHex) {
			sb.append(String.format("%02X", (int) c));
		}
		return sb.toString();

	}

	public static final IPad BCD_PAD_F_AFTER = new IPad() {

		@Override
		public String pad(String src) {
			if (src.length() % 2 == 0) {
				return src;
			}
			return src + "F";
		}
	};
	public static final IPad BCD_PAD_0_BEFORE = new IPad() {

		@Override
		public String pad(String src) {
			if (src.length() % 2 == 0) {
				return src;
			}
			return "0" + src;
		}
	};

	public static byte[] str2bcd(String asc) {
		return str2bcd(asc, BCD_PAD_F_AFTER);
	}

	/**
	 * 
	 * @param dest
	 * @param pos
	 * @param asc
	 * @param pad
	 * @return
	 */
	public static byte[] str2bcd(byte[] dest, int pos, String asc, IPad pad) {
		asc = pad.pad(asc);
		int len = asc.length() / 2;
		byte[] src = asc.getBytes();
		int index = 0;
		for (int i = 0; i < len; ++i) {
			int destIndex = pos + i;
			int high = asc2bcd(src[index++]);
			dest[destIndex] = (byte) (asc2bcd(src[index++]) | (high << 4));
		}
		return dest;
	}

	private static byte asc2bcd(byte asc) {
		byte bcd;
		if ((asc >= '0') && (asc <= '9'))
			bcd = (byte) (asc - '0');
		else if ((asc >= 'A') && (asc <= 'F'))
			bcd = (byte) (asc - 'A' + 10);
		else if ((asc >= 'a') && (asc <= 'f'))
			bcd = (byte) (asc - 'a' + 10);
		else
			bcd = (byte) (asc - 48);
		return bcd;
	}

	public static byte[] str2bcd(String asc, IPad pad) {
		byte[] result = new byte[asc.length() % 2 == 0 ? asc.length() / 2 : asc.length() / 2 + 1];
		return str2bcd(result, 0, asc, pad);
	}

	/**
	 * 异或和
	 * 
	 * @return
	 */
	public static byte xorsum(byte[] src, int len) {
		byte result = 0;
		for (int i = 0; i < len; ++i) {
			result ^= src[i];
		}
		return result;
	}

	public static String padLeftZero(String value, int len) {
		if (value == null || value.equals("null")) {
			value = "";
		}
		int valueLen = value.length();
		if (valueLen < len) {

			StringBuilder stringBuilder = new StringBuilder();
			for (int i = valueLen; i < len; ++i) {
				stringBuilder.append("0");
			}
			stringBuilder.append(value);
			value = stringBuilder.toString();
		} else if (valueLen > len) {
			throw new RuntimeException("String " + value + " length is greater than " + len);
		}

		return value;
	}

	public static String padRightZero(String value, int len) {
		if (value == null || value.equals("null")) {
			value = "";
		}
		int valueLen = value.length();
		if (valueLen < len) {
			StringBuilder stringBuilder = new StringBuilder(value);
			for (int i = valueLen; i < len; ++i) {
				stringBuilder.append("0");
			}
			value = stringBuilder.toString();
		} else if (valueLen > len) {
			throw new RuntimeException("String " + value + " length is greater than " + len);
		}
		return value;
	}

	public static String toHexString(int n) {
		byte[] bytes = new byte[4];

		bytes[0] = (byte) (n & 0xff);
		bytes[1] = (byte) ((n >> 8) & 0xff);
		bytes[2] = (byte) ((n >> 16) & 0xff);
		bytes[3] = (byte) ((n >> 24) & 0xff);

		return HexUtil.encodeHexStr(bytes);
	}
}
